package component;

import java.util.ArrayList;

import Prism.core.AbstractImplementation;
import Prism.core.Event;
import Prism.core.PrismConstants;
import basicTypes.UserRecord;

// All Prov handle functions are private, because this functions are called by only handle() and not by other outside instance
// All request reply(reply of req) is private, because this functions are called by only handle() and not by other outside instance
// checkInvariant is public because outside of this instance can check the invariant property for security reason

public class BillingSystem extends AbstractImplementation{
	
	// prov Name(Event Name)
	public static final String provShowBill = "showBill";
	public static final String provPrintNewBills = "printNewBills";
	
	// bills: \set OffLineRecordType;
	ArrayList<UserRecord> bills;
	
	public BillingSystem() {
		//Create bill array
		bills = new ArrayList<UserRecord>();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	// Handle of reply of Send or request from bottom layer
	public void handle(Prism.core.Event e) {
		
		// Handle of reply of request
		if (e.getName().equals(OffLineDatabase.provGetBills)) {
			getBillsReplyHandle(e);

		}
		// Handle of request of provShowBill
		else if (e.getName().equals(provShowBill)) {
			String id = (String) e.getParameter("phoneNumber");
			UserRecord bill = showBill(id);
			String result;
			Event n = new Event(provShowBill, PrismConstants.REPLY);
			
			if(bill != null) {
				result = "Name: " + bill.getName() + ", Phone#: " + bill.getPhoneNumber() + ", $"+ bill.getAmount();
			}
			else {
				result = "INPUT WRONG";
			}
			n.addParameter("result", result);
			
			
			this.send(n);
		}
		// Handle of request of provPrintNewBills
		else if (e.getName().equals(provPrintNewBills)) {
			int tp = Integer.parseInt((String)e.getParameter("timePeriod"));
			printNewBills(tp);
		}
	}
	
	// Check invariant property
	public boolean checkInvariant() {
		// check bills size
		if(bills.size()>=0) return true;
		return false;
	}
	
	// billByPhoneNumber: PhoneNumber -> OffLineRecordType;
	public UserRecord billByPhoneNumber(String phoneNumber) {
		int size = bills.size();
		String no;
		// Find bill by PhoneNumber
		for(int i=0; i<size; i++) {
			// if found, return it
			no = bills.get(i).getPhoneNumber();
			if(no.equals(phoneNumber)) return bills.get(i);
		}
		
		// in case of no bill found
		return null; 
	}
	
	//	prov i1: showBill(id: PhoneNumber): OffLineRecordType;
	private UserRecord showBill(String id) {
		return billByPhoneNumber(id);
	}
	
	// prov i2: printNewBills(tp: TimePeriod); //print all new bills for a given time period
	private void printNewBills(int tp) {
		// DO PRINT
	}
	
	// req r1: getBills(): \set OffLineRecordType;
	// this is public, because outside of this instance can invoke this
	public void getBills() {
		// send request to upper layer
		Prism.core.Event r = new Prism.core.Event(OffLineDatabase.provGetBills, PrismConstants.REQUEST);
		this.send(r);
	}
	
	// handle of reply of getbills()'s request 
	private void getBillsReplyHandle(Prism.core.Event e) {
		UserRecord tempRecord = new UserRecord(
				(String) e.getParameter("time"),
				(String) e.getParameter("name"),
				(String) e.getParameter("amount"),
				(String) e.getParameter("phoneNumber")
				);
		
		String clearFlag = (String) e.getParameter("clearFlag");

		// Clear if data is first one of whole billing
		if(clearFlag.equals("true")) bills.clear();
		
		// Add billing
		bills.add(tempRecord);
	}
	
}
